﻿using Core.DataAccess.Model.iTool.Gateway;
using Core.Framework;
using Core.Framework.Common;
using Environment.Interfaces;
using HandlerContextOptions;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Environment.Implements
{
    public class GatewatDateBase : IGatewatDateBase
    {
        readonly string gatewaydatabasePath = "../gatewaydatabase.config";
        string _iToolConfigurePath { get; } = "/var/iTool/.iToolConfig";
        string _iToolStartTypePath { get; } = "/var/iTool/local.s";
        const string localIp = "127.0.0.1";
        IToolCenter toolCenter { get; }

        public GatewatDateBase(IToolCenter toolCenter)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                _iToolConfigurePath = $"D:/iTool-Cloud-Framework/.iToolConfig";
                _iToolStartTypePath = "D:/iTool-Cloud-Framework/local.s";
            }


            this.toolCenter = toolCenter;
        }

        public async Task<CreateGatewayDateBaseResult> CreateInstance(CreateGatewayDateBaseRequset options)
        {

            var result = new CreateGatewayDateBaseResult();

            if (string.IsNullOrWhiteSpace(options.AdministratorPassward) || options.AdministratorPassward.Length < 9)
            {
                result.Description = "Administrator Passward is null or Passward length < 9";
                return result;
            }

            if (string.IsNullOrWhiteSpace(options.Passward) || options.Passward.Length < 9)
            {
                result.Description = "SA Passward is null or Passward length < 9";
                return result;
            }

            if (options.Port < 1000 && options.Port > 65535)
            {
                result.Description = "createDataBaseRequest Port > 1000 && createDataBaseRequest Port < 65535";
                return result;
            }

            try
            {
                await Shell.ExcuterAsync($"docker rm iToolGatewaySqlServer");
                await Task.Delay(1000);
                await Shell.ExcuterAsync($"docker run --restart=always -e ACCEPT_EULA=Y -e MSSQL_PID=\"Developer\" -e MSSQL_SA_PASSWORD=\"{options.Passward}\" -e MSSQL_TCP_PORT={options.Port} -p {options.Port}:{options.Port} --name iToolGatewaySqlServer -d microsoft/mssql-server-linux");

                await Task.Delay(2000);
                if (this.CheckConnection(options.Passward, options.Port))
                {
                    options.Seed = new Random().Next(111_111, 9999_999).ToString();
                    options.Seed += $"{DateTime.Now.Hour}+{DateTime.Now.Millisecond}";

                    AppSetting.SetiToolGatewayOptions(options.SqlConnection, options.Seed);

                    try
                    {
                        await this.EnsureCreated(options);
                        await this.LoacalAutoStart(options);
                        result.IsSuccessful = true;
                    }
                    catch (Exception ex)
                    {
                        result.Description = ex.Message;
                    }

                    // try connection
                    System.IO.File.Create(gatewaydatabasePath).Close();
                    System.IO.File.WriteAllText(gatewaydatabasePath, JsonConvert.SerializeObject(options));
                    return result;
                }
                else
                {
                    result.Description = "数据库启动失败。请检查Docker是否登录，microsoft/mssql-server-linux 镜像是否成功获取。容器启动失败原因";
                    return result;
                }
            }
            catch (Exception ex)
            {
                if (System.IO.File.Exists(gatewaydatabasePath))
                {
                    System.IO.File.Delete(gatewaydatabasePath);
                }
                result.IsSuccessful = false;
                result.Description = ex.Message;
                return result;
            }
        }


        /// <summary>
        /// 配置数据库
        /// </summary>
        /// <param name="options"></param>
        /// <returns></returns>
        public async Task<bool> EnsureCreated(CreateGatewayDateBaseRequset options)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                try
                {
                    // 删除
                    //context.Database.EnsureDeleted();
                    // 没有则创建
                    context.Database.EnsureCreated();
                    // 执行迁移
                    context.Database.Migrate();

                    // 处理初始化数据
                    var list = context.SysRoles.ToList();

                    // 是否是第一次创建
                    if (list.Count == 0)
                    {
                        // 添加角色
                        context.SysRoles.Add(new SysRole
                        {
                            RoleName = "超级管理员",
                            RoleRemark = "创建项目用户",
                            RoleIsDelete = 0,
                            RoleCreateTime = DateTime.Now
                        });
                        // 添加超级用户
                        context.SysUsers.Add(new SysUser
                        {
                            UserName = "超级管理员",
                            UserLoginName = "administrator",
                            UserEmail = options.Email,
                            UserPwd = options.AdministratorPassward,
                            UserIsDelete = 0,
                            UserCreateTime = DateTime.Now
                        });
                        await context.SaveChangesAsync();

                        // 发送邮件通知
                        EmailUtils.Send(options.Email,
                            "运行环境初始化成功",
                            "<p>管理员账户名：administrator<p>" +
                            $"<p>管理员登录密码：{options.AdministratorPassward}<p>" +
                            $"<p>数据库密码：{options.Passward}<p>" +
                            $"<p>请妥善保管您的账户。感谢您的支持<p>"
                            );

                        // 设置用户角色
                        var roleid = await context.SysRoles.Select(item => item.RoleId).FirstAsync();
                        var userid = await context.SysUsers.Select(item => item.UserId).FirstAsync();

                        // Map
                        context.SysUserRoles.Add(new SysUserRole
                        {
                            UserRoleRoleId = roleid,
                            UserRoleUserId = userid,
                            UserRoleCreateTime = DateTime.Now
                        });
                        await context.SaveChangesAsync();

                        context.SysMenus.Add(new SysMenu
                        {
                            MenuName = "Monitor Panel",
                            MenuIcon = "dot-chart",
                            MenuIsShow = true,
                            MenuCreateTime = DateTime.Parse("1994-03-12 00:00:00.000")
                        });
                        await context.SaveChangesAsync();

                        List<SysMenu> sysMenus = this.GetSysParentMenus();
                        // 设置父菜单信息
                        context.SysMenus.AddRange(sysMenus);
                        await context.SaveChangesAsync();

                        var monitorid = await context.SysMenus.Where(item => item.MenuName == "Monitor Panel").Select(item => item.MenuId).FirstAsync();
                        var monitorMenuChilds = this.GetMonitorMenus(monitorid);

                        var loggerid = await context.SysMenus.Where(item => item.MenuName == "Logger Center").Select(item => item.MenuId).FirstAsync();
                        var loggerMenuChilds = this.GetSysLoggerMenus(loggerid);
                        var settingid = await context.SysMenus.Where(item => item.MenuName == "Setting").Select(item => item.MenuId).FirstAsync();
                        var settinMenuChilds = this.GetSysSettingMenus(settingid);
                        var gatewayid = await context.SysMenus.Where(item => item.MenuName == "Api Gateway").Select(item => item.MenuId).FirstAsync();
                        var gatewayMenuChilds = this.GetSysGatewayMenus(gatewayid);

                        context.SysMenus.AddRange(monitorMenuChilds);
                        context.SysMenus.AddRange(loggerMenuChilds);
                        context.SysMenus.AddRange(settinMenuChilds);
                        context.SysMenus.AddRange(gatewayMenuChilds);

                        await context.SaveChangesAsync();

                    }

                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 本地一键启动
        /// </summary>
        /// <returns></returns>
        public async Task<bool> LoacalAutoStart(CreateGatewayDateBaseRequset options)
        {
            try
            {
                // 检查是否是一键安装 【本地文件】 if
                if (!File.Exists(_iToolStartTypePath))
                {
                    return true;
                }

                string password = this.GeneratePassword();
                // create mongo
                string command = $"docker run --name iToolMongoDB -p 27017:27017 -v /data/mongo:/data/db -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD={password} -d mongo";
                await Shell.ExcuterAsync(command);

                // create elasticsearch
                command = $"docker run --name iToolElasticsearch -d -e ES_JAVA_OPTS=\"-Xms512m -Xmx512m\" -e \"discovery.type=single-node\" -p 9200:9200 -p 9300:9300 elasticsearch:7.12.1";
                await Shell.ExcuterAsync(command);

                // 创建数据库
                foreach (var item in new string[] { "iToolBase", "Logger", "Function", "Message", "CustomData" })
                {
                    var check = new CheckDatabases(options.Port, options.Passward, item);
                    await check.CreateDatabaseStructer();
                }

                // 稍等一下服务启动
                await Task.Delay(1000);

                // 将实例写入数据库
                await this.SaveServiceInstanceAsync(options, password);

                // 服务写入实例
                var configOptions = await this.GetConfigAsync();
                await this.toolCenter.Edit(configOptions);

                //写入配置文件
                var iToolConfigure = await this.toolCenter.GetiToolConfigure(configOptions);
                var iToolConfigureString = iToolConfigure.TryToJson();
                if (!File.Exists(_iToolConfigurePath))
                {
                    File.Create(_iToolConfigurePath).Close();
                }
                File.WriteAllText(_iToolConfigurePath, iToolConfigureString);


                // 启动Service
                command = $"systemctl start iToolServer.service";
                await Shell.ExcuterAsync(command);
                // 启动文件服务器
                command = $"systemctl start iToolFileCenter.service";
                await Shell.ExcuterAsync(command);
                // 稍等一下服务启动
                await Task.Delay(2000);
                // 启动客户端
                command = $"systemctl start iToolConnector.service";
                await Shell.ExcuterAsync(command);

                return true;
            }
            catch (Exception)
            {
                throw;
            }

        }


        // 检查数据连接是否有效
        private bool CheckConnection(string pwd, int port, int isRetryIndex = 0)
        {
            string conneciotnString = $"Data Source=127.0.0.1,{port};database=master;uid=sa;pwd={pwd};MultipleActiveResultSets=true;Connect Timeout=2";

            try
            {
                using (var connection = new SqlConnection(conneciotnString))
                {
                    connection.Open();
                }
                return true;
            }
            catch (Exception)
            {
                if (isRetryIndex < 5)
                {
                    isRetryIndex++;
                    Thread.Sleep(isRetryIndex * 1000);
                    return CheckConnection(pwd, port, isRetryIndex);
                }

                return false;
            }
        }

        private List<SysMenu> GetMonitorMenus(int parentid)
        {
            List<SysMenu> sysMenus = new List<SysMenu>();
            sysMenus.Add(new SysMenu
            {
                MenuName = "Overview",
                MenuUrl = "/"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Cloud Analysis",
                MenuUrl = "/cluster/overview"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Cloud Noder",
                MenuUrl = "/cluster/noder"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Cloud Service",
                MenuUrl = "/cluster/services"
            });

            foreach (var item in sysMenus)
            {
                item.MenuIsShow = true;
                item.MenuCreateTime = DateTime.Parse("1994-03-12 00:00:00.000");
                item.MenuParentId = parentid;
            }
            return sysMenus;
        }

        private List<SysMenu> GetSysLoggerMenus(int parentid)
        {
            List<SysMenu> sysMenus = new List<SysMenu>();
            sysMenus.Add(new SysMenu
            {
                MenuName = "System",
                MenuUrl = "/logger/system"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Function",
                MenuUrl = "/logger/function"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Gateway",
                MenuUrl = "/logger/gateway"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Client",
                MenuUrl = "/logger/client"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Tracking",
                MenuUrl = "/logger/tracking"
            });

            sysMenus.Add(new SysMenu
            {
                MenuName = "Custom",
                MenuUrl = "/logger/custom"
            });

            foreach (var item in sysMenus)
            {
                item.MenuIsShow = true;
                item.MenuCreateTime = DateTime.Parse("1994-03-12 00:00:00.000");
                item.MenuParentId = parentid;
            }
            return sysMenus;
        }

        private List<SysMenu> GetSysSettingMenus(int parentid)
        {
            List<SysMenu> sysMenus = new List<SysMenu>();
            sysMenus.Add(new SysMenu
            {
                MenuName = "User",
                MenuUrl = "/setting/user"
            });
            sysMenus.Add(new SysMenu
            {
                MenuName = "Role",
                MenuUrl = "/setting/role"
            });

            foreach (var item in sysMenus)
            {
                item.MenuIsShow = true;
                item.MenuCreateTime = DateTime.Parse("1994-03-12 00:00:00.000");
                item.MenuParentId = parentid;
            }
            return sysMenus;
        }

        private List<SysMenu> GetSysGatewayMenus(int parentid)
        {
            List<SysMenu> sysMenus = new List<SysMenu>();
            sysMenus.Add(new SysMenu
            {
                MenuName = "Global",
                MenuUrl = "/gateway/global"
            });
            sysMenus.Add(new SysMenu
            {
                MenuName = "Router",
                MenuUrl = "/gateway/router"
            });

            foreach (var item in sysMenus)
            {
                item.MenuIsShow = true;
                item.MenuCreateTime = DateTime.Parse("1994-03-12 00:00:00.000");
                item.MenuParentId = parentid;
            }
            return sysMenus;
        }

        private List<SysMenu> GetSysParentMenus()
        {
            List<SysMenu> sysMenus = new List<SysMenu>();

            sysMenus.Add(new SysMenu
            {
                MenuName = "Server Nodes",
                MenuUrl = "/servers",
                MenuIcon = "cloud-server"
            });
            sysMenus.Add(new SysMenu
            {
                MenuName = "Api Gateway",
                MenuIcon = "gateway"
            });
            sysMenus.Add(new SysMenu
            {
                MenuName = "iTool Center",
                MenuUrl = "/itool",
                MenuIcon = "control"
            });
            sysMenus.Add(new SysMenu
            {
                MenuName = "Nginx",
                MenuUrl = "/nginx",
                MenuIcon = "apartment"
            });
            sysMenus.Add(new SysMenu
            {
                MenuName = "Logger Center",
                MenuIcon = "file-search"
            });
            sysMenus.Add(new SysMenu
            {
                MenuName = "Setting",
                MenuIcon = "setting"
            });

            foreach (var item in sysMenus)
            {
                item.MenuIsShow = true;
                item.MenuCreateTime = DateTime.Parse("1994-03-12 00:00:00.000");
            }

            return sysMenus;
        }

        private async Task SaveServiceInstanceAsync(CreateGatewayDateBaseRequset options, string pwd)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                // sql server
                var ServiceInstance = new ServiceInstance();
                ServiceInstance.NodeIP = localIp;
                ServiceInstance.Title = "iToolGatewaySqlServer";
                ServiceInstance.NodeId = 1;
                ServiceInstance.User = "sa";
                ServiceInstance.Password = options.Passward;
                ServiceInstance.Port = options.Port;
                ServiceInstance.Type = "iToolBase,Function,Message,Logger,CustomData";
                ServiceInstance.Status = 2;
                ServiceInstance.IsRunInDocker = true;
                ServiceInstance.Process = "SqlServer";
                ServiceInstance.LastCheckTime = DateTime.UtcNow;
                await context.ServiceInstances.AddAsync(ServiceInstance);

                // MongoDB
                var mongoServiceInstance = new ServiceInstance();
                mongoServiceInstance.NodeIP = localIp;
                mongoServiceInstance.Title = "iToolMongoDB";
                mongoServiceInstance.NodeId = 1;
                mongoServiceInstance.User = "admin";
                mongoServiceInstance.Password = pwd;
                mongoServiceInstance.Port = 27017;
                mongoServiceInstance.Status = 2;
                mongoServiceInstance.IsRunInDocker = true;
                mongoServiceInstance.Process = "MongoDB";
                mongoServiceInstance.LastCheckTime = DateTime.UtcNow;
                await context.ServiceInstances.AddAsync(mongoServiceInstance);

                //Elasticsearch
                var esServiceInstance = new ServiceInstance();
                esServiceInstance.NodeIP = localIp;
                esServiceInstance.Title = "iToolElasticsearch";
                esServiceInstance.NodeId = 1;
                esServiceInstance.User = "admin";
                esServiceInstance.Password = pwd;
                esServiceInstance.Port = 9200;
                esServiceInstance.Status = 2;
                esServiceInstance.IsRunInDocker = true;
                esServiceInstance.Process = "Elasticsearch";
                esServiceInstance.LastCheckTime = DateTime.UtcNow;
                await context.ServiceInstances.AddAsync(esServiceInstance);

                //iTool-Server
                var iToolServerServiceInstance = new ServiceInstance();
                iToolServerServiceInstance.NodeIP = localIp;
                iToolServerServiceInstance.Title = "iTool-Server";
                iToolServerServiceInstance.NodeId = 1;
                iToolServerServiceInstance.Status = 2;
                iToolServerServiceInstance.IsRunInDocker = false;
                iToolServerServiceInstance.Process = "iToolService";
                iToolServerServiceInstance.LastCheckTime = DateTime.UtcNow;
                await context.ServiceInstances.AddAsync(iToolServerServiceInstance);

                //iTool-FileApi
                var iToolFileApiServiceInstance = new ServiceInstance();
                iToolFileApiServiceInstance.NodeIP = localIp;
                iToolFileApiServiceInstance.Title = "iTool-FileApi";
                iToolFileApiServiceInstance.NodeId = 1;
                iToolFileApiServiceInstance.Status = 2;
                iToolFileApiServiceInstance.IsRunInDocker = false;
                iToolFileApiServiceInstance.Process = "iToolFileCenter";
                iToolFileApiServiceInstance.LastCheckTime = DateTime.UtcNow;
                await context.ServiceInstances.AddAsync(iToolFileApiServiceInstance);

                //iTool-Connector
                var iToolConnectorServiceInstance = new ServiceInstance();
                iToolConnectorServiceInstance.NodeIP = localIp;
                iToolConnectorServiceInstance.Title = "iTool-Connector";
                iToolConnectorServiceInstance.NodeId = 1;
                iToolConnectorServiceInstance.Status = 2;
                iToolConnectorServiceInstance.IsRunInDocker = false;
                iToolConnectorServiceInstance.Process = "iToolClient";
                iToolConnectorServiceInstance.LastCheckTime = DateTime.UtcNow;
                await context.ServiceInstances.AddAsync(iToolConnectorServiceInstance);

                await context.SaveChangesAsync();
            }
        }

        private async Task<iToolCenterOptionRequset> GetConfigAsync()
        {
            List<ServiceInstance> list = null;
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                list = await context.ServiceInstances.ToListAsync();
            }

            var sqlid = list.Find(item => item.Process == "SqlServer").ServiceInstanceId.ToString();
            var esid = list.Find(item => item.Process == "Elasticsearch").ServiceInstanceId.ToString();
            var mdid = list.Find(item => item.Process == "MongoDB").ServiceInstanceId.ToString();

            return new iToolCenterOptionRequset
            {
                Port = 12990,
                ServicePort = 12991,
                ServiceGatewayPort = 12992,
                SoBacklog = 100,
                HeartbeatTimeOutSeconds = 10,
                BaseSQLServer = new List<string>() { sqlid },
                FnsSQLServer = new List<string>() { sqlid },
                MsgSQLServer = new List<string>() { sqlid },
                LogSQLServer = new List<string>() { sqlid },
                ElasticSearch = new List<string>() { esid },
                MongoDB = new List<string>() { mdid },

            };
        }

        private string GeneratePassword()
        {
            StringBuilder sb = new StringBuilder(12);
            for (int i = 0; i < 10; i++)
            {
                if (i == 5)
                {
                    sb.Append(new Random(i + int.Parse(DateTime.Now.ToString("fffffff"))).Next(100, 956));
                }
                else if (i % 2 == 0)
                {
                    sb.Append(Convert.ToChar(new Random(i + int.Parse(DateTime.Now.ToString("fffffff"))).Next(65, 91)));
                }
                else
                {
                    sb.Append(Convert.ToChar(new Random(i + int.Parse(DateTime.Now.ToString("fffffff"))).Next(97, 123)));
                }
            }

            return sb.ToString();
        }

    }
}
